﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using Autofac;
using Autofac.Configuration;
using ContainerVariations.Common;
using NUnit.Framework;

namespace ContainerVariations.AutofacSamples
{
    [TestFixture]
    public class AutoFacFixture : TemplateFixture
    {
        [Test]
        public override void Configure_a_single_object_with_container_lifetime()
        {
            var builder = new ContainerBuilder();
            builder.RegisterType<Knob>().As<IKnob>().SingleInstance();
            using (IContainer container = builder.Build())
            {
                var knobA = container.Resolve<IKnob>();
                var knobB = container.Resolve<IKnob>();
                Assert.That(knobA, Is.EqualTo(knobB));
            }
        }

        [Test]
        public override void Configure_a_single_object_with_transient_lifetime()
        {
            var builder = new ContainerBuilder();
            builder.RegisterType<Knob>().As<IKnob>();
            using (IContainer container = builder.Build())
            {
                var knobA = container.Resolve<IKnob>();
                var knobB = container.Resolve<IKnob>();
                Assert.That(knobA, Is.Not.EqualTo(knobB));
            }
        }

        [Test]
        public override void Configure_a_manually_wired_dependency_graph()
        {
            var builder = new ContainerBuilder();
            builder.Register(c => new House(c.Resolve<IDoor>())).As<IHouse>();
            builder.Register(c => new Door(c.Resolve<IKnob>())).As<IDoor>();
            builder.Register(c => new Knob()).As<IKnob>();
            using (IContainer container = builder.Build())
            {
                var house = container.Resolve<IHouse>();
                Assert.That(house, Is.Not.Null);
                house.OpenDoor();
            }
        }

        [Test]
        public override void Configure_an_autowired_dependency_graph()
        {
            var builder = new ContainerBuilder();
            builder.RegisterType<House>().As<IHouse>();
            builder.RegisterType<Door>().As<IDoor>();
            builder.RegisterType<Knob>().As<IKnob>();
            using (IContainer container = builder.Build())
            {
                var house = container.Resolve<IHouse>();
                Assert.That(house, Is.Not.Null);
                house.OpenDoor();
            }
        }

        [Test]
        public override void Xml_configure_an_autowired_dependency_graph()
        {
            var builder = new ContainerBuilder();
            builder.RegisterModule(new ConfigurationSettingsReader("mycomponents"));
            using (IContainer container = builder.Build())
            {
                var house = container.Resolve<IHouse>();
                Assert.That(house, Is.Not.Null);
                house.OpenDoor();
            }
        }
        
        [Test]
        public override void Configure_container_dynamically_supplemented_with_xml()
        {
            var builder = new ContainerBuilder();
            builder.RegisterType<Apple>();
            builder.RegisterType<AppleSeeds>().As<IAppleContents>();
            builder.RegisterModule(new ConfigurationSettingsReader("mycomponents"));
            using (IContainer container = builder.Build())
            {
                Assert.That(container.Resolve<Apple>(), Is.TypeOf(typeof (Apple)));
                Assert.That(container.Resolve<Orange>(), Is.TypeOf(typeof (Orange)));
            }
        }

        [Test]
        public override void Configure_container_dynamically_modified_by_xml()
        {
            var builder = new ContainerBuilder();
            builder.RegisterType<Apple>();
            // This registration (AppleSeeds) should be replaced by the Worm
            //   component registration in the app.config file because the
            //   app.config registrations are applied last, and the last
            //   registered is the default.
            builder.RegisterType<AppleSeeds>().As<IAppleContents>();
            builder.RegisterModule(new ConfigurationSettingsReader("mycomponents"));
            using (IContainer container = builder.Build())
            {
                var apple = container.Resolve<Apple>();
                Assert.That(apple.ToString(), Is.EqualTo("Apple contains: Worm"));
            }
        }

        [Test]
        public override void Configure_an_open_generic()
        {
            var builder = new ContainerBuilder();
            builder.RegisterGeneric(typeof(PassthruFlattener<>)).As(typeof(IFlattener<>));
            builder.RegisterType<ContactFlattener>().As(typeof(IFlattener<Contact>));
//            builder.Register(typeof(ContactFlattener)).As(typeof(IFlattener<Contact>));
            using (IContainer container = builder.Build())
            {
                var contactFlattener = container.Resolve<IFlattener<Contact>>();
                var addressFlattener = container.Resolve<IFlattener<Address>>();
                Assert.That(contactFlattener, Is.TypeOf(typeof(ContactFlattener)));
                Assert.That(addressFlattener, Is.TypeOf(typeof(PassthruFlattener<Address>)));
            }
        }

        [Test]
        public override void Configure_a_type_with_a_lambda_or_delegate_dependency()
        {
            string actualValue = null;

            var builder = new ContainerBuilder();
            builder.Register(c => new TestActor(x => actualValue = x)).As<ITestActor>();
            using (IContainer container = builder.Build())
            {
                var actor = container.Resolve<ITestActor>();
                const string expectedValue = "This is the value we're assigning.";
                actor.Act(expectedValue);

                Assert.That(actualValue, Is.EqualTo(expectedValue));
            }
        }

        [Test]
        public override void Configure_all_classes_of_a_specified_type_in_a_specified_assembly()
        {
            Assembly widgetAssembly = typeof (IWidget).Assembly;
            var builder = new ContainerBuilder();
            builder.RegisterAssemblyTypes(widgetAssembly)
                .Where(t => typeof (IWidget).IsAssignableFrom(t))
                .As<IWidget>();

            using (IContainer container = builder.Build())
            {
                var widgets = container.Resolve<IEnumerable<IWidget>>();

                foreach (var widget in widgets)
                {
                    Console.WriteLine(widget.GetType().Name + ": " + widget.Call());
                }
                Assert.That(widgets.Count(), Is.EqualTo(4));
            }
        }

        [Test]
        public override void Configure_a_class_with_multiple_decorators()
        {
            var builder = new ContainerBuilder();

            builder.RegisterInstance(new DoorUser(true)).As<IDoorUser>();
            builder.RegisterType<Knob>().As<IKnob>();
            builder.RegisterType<Door>().Named<IDoor>("base-door");
            builder.Register(c => new SecureDoor(c.Resolve<IDoor>("base-door"), c.Resolve<IDoorUser>()))
                .Named<IDoor>("secure-door");
            builder.Register(c => new EmailDoor(c.Resolve<IDoor>("secure-door"))).As<IDoor>();

            using (IContainer container = builder.Build())
            {
                var door = container.Resolve<IDoor>();
                door.Open();
            }
        }

        [Test]
        public override void Configure_a_class_with_scalar_dependencies_from_configuration_api()
        {
            var builder = new ContainerBuilder();

            bool mayOpenDoor = Convert.ToBoolean(ConfigurationManager.AppSettings["mayOpenDoor"]);

            builder.Register(c => new DoorUser(mayOpenDoor)).As<IDoorUser>();

            using (IContainer container = builder.Build())
            {
                var doorUser = container.Resolve<IDoorUser>();
                Assert.That(doorUser.MayOpenDoor, Is.EqualTo(mayOpenDoor));
            }
        }

        [Test]
        public override void Container_can_use_an_instance_created_outside_the_container()
        {
            var stringBuilder = new StringBuilder();
            var writer = new StringWriter(stringBuilder);

            var builder = new ContainerBuilder();
            builder.RegisterInstance<TextWriter>(writer);
            builder.RegisterType<TextWriterSender>().As<ISender>();

            using (IContainer container = builder.Build())
            {
                var sender = container.Resolve<ISender>();
                const string message = "This is the message to send.";
                sender.Send(message);
                Assert.That(stringBuilder.ToString(), Is.EqualTo(message));
            }
        }

        [Test]
        public override void Disposable_classes_with_container_lifetime_are_disposed_when_container_is_disposed()
        {
            IKnob knob;
            var builder = new ContainerBuilder();
            builder.Register(c => new Knob()).As<IKnob>();
            using (IContainer container = builder.Build())
            {
                knob = container.Resolve<IKnob>();
                Assert.That(knob.IsDisposed, Is.False);
            }
            Assert.That(knob.IsDisposed, Is.True);
        }

        [Test]
        public override void Disposable_classes_with_transient_lifetime_are_disposed_when_container_is_disposed()
        {
            IKnob knob;
            var builder = new ContainerBuilder();
            builder.Register(c => new Knob()).As<IKnob>().InstancePerDependency();
            using (IContainer container = builder.Build())
            {
                knob = container.Resolve<IKnob>();
                Assert.That(knob.IsDisposed, Is.False);
            }
            Assert.That(knob.IsDisposed, Is.True);
        }

        [Test]
        public override void Register_components_with_scalar_dependencies()
        {
            var builder = new ContainerBuilder();
            builder.RegisterType<Answer>()
                .WithParameter(new NamedParameter("value", 42));
            using (IContainer container = builder.Build())
            {
                Assert.That(container.Resolve<Answer>().Value, Is.EqualTo(42));
            }
        }

        [Test]
        public override void Resolve_components_providing_scalar_dependencies()
        {
            var builder = new ContainerBuilder();
            builder.RegisterType<Answer>();
            using (IContainer container = builder.Build())
            {
                var answer = container.Resolve<Answer>(new NamedParameter("value", 42));
                Assert.That(answer.Value, 
                    Is.EqualTo(42));
            }
        }

        [Test]
        public void Delegate_factory()
        {
            var builder = new ContainerBuilder();
            builder.RegisterType<NameAnalysis>().InstancePerDependency();
            builder.RegisterType<NameDataLookupService>().As<INameDataLookupService>();
            using (IContainer container = builder.Build())
            {
                var factory = container.Resolve<NameAnalysis.Factory>();
                var name = factory.Invoke("Larry");
            }
        }

    }
}